/*
 * Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "sle_ssap_client_server.h"
#include "sle_ssap_client_service.h"
#include "sle_adapter_manager.h"
#include "sle_utils_remote_observer_list.h"
#include "sle_hilog.h"
#include "sle_client_event_handler.h"
#include "sle_advanced_setting_manager.h"

namespace OHOS {
namespace NearLink {
namespace SleStandard {
namespace {
constexpr const char *SLE_CLIENT_RUNNER_WORK_THREAD = "SLE_CLIENT_RUNNER_WORK_THREAD";
}

class SleSsapClientServer::SleSsapClientServerImpl {
public:
    SleSsapClientServerImpl();
    ~SleSsapClientServerImpl();

    SleErrorCode CheckOperation();
    SleErrorCode RegisterSsapClient(const Uuid &uuid, uint8_t &clientId, const sptr<ISleSsapClientCallback> &callback);
    SleErrorCode UnregisterSsapClientInner(const sptr<ISleSsapClientCallback> &callback, uint8_t clientId);
    SleErrorCode UnregisterSsapClient(uint8_t clientId, const sptr<ISleSsapClientCallback> &callback);
    SleErrorCode FindSsapStructure(uint8_t clientId, uint16_t connectId, const SsapClientFindStructure &param);
    SleErrorCode ReadRequestByUuid(uint8_t clientId, uint16_t connectId, const SsapClientReadReqByUUID &param);
    SleErrorCode ReadRequest(uint8_t clientId, uint16_t connectId, uint16_t handle, uint8_t type);
    SleErrorCode WriteRequest(uint8_t clientId, uint16_t connectId, const SsapClientHandleValue &value);
    SleErrorCode WriteCommand(uint8_t clientId, uint16_t connectId, const SsapClientHandleValue &value);
    SleErrorCode ExchangeInfo(uint8_t clientId, uint16_t connectId, uint32_t mtu, uint16_t version);

    class SleSsapClientCallbackImpl;
    std::unique_ptr<SleSsapClientCallbackImpl> callbackImpl_ = nullptr;

    SleSsapClientService *clientService_ = nullptr;
    SleAdapterManager *adapterManager_ = nullptr;
    SleRemoteObserverList<ISleSsapClientCallback, uint8_t> callbacks_;
    std::shared_ptr<AppExecFwk::EventRunner> clientEventRunner_ = nullptr;
    std::shared_ptr<SleClientEventHandler> clientEventHandler_  = nullptr;
};

class SleSsapClientServer::SleSsapClientServerImpl::SleSsapClientCallbackImpl : public ISsapClientCallback {
public:
    SleSsapClientCallbackImpl(SleSsapClientServer::SleSsapClientServerImpl &impl) : impl_(impl){};
    ~SleSsapClientCallbackImpl() override = default;

    void OnFindService(
        uint8_t clientId, uint16_t connectId, const ClientFindServiceRes &result, SleErrorCode errCode) override
    {
        SLE_LOGD("OnFindService In");
        SsapClientFindServiceRes res(result);
        impl_.callbacks_.ForEach([clientId, connectId, &res, errCode](sptr<ISleSsapClientCallback> callback) {
            callback->OnFindService(clientId, connectId, res, errCode);
        });
    }

    void OnFindProperty(
        uint8_t clientId, uint16_t connectId, const ClientFindPropertyRes &result, SleErrorCode errCode) override
    {
        SLE_LOGD("OnFindProperty In");
        SsapClientFindPropertyRes res(result);
        impl_.callbacks_.ForEach([clientId, connectId, &res, errCode](sptr<ISleSsapClientCallback> callback) {
            callback->OnFindProperty(clientId, connectId, res, errCode);
        });
    }

    void OnFindComplete(
        uint8_t clientId, uint16_t connectId, const ClientFindStructureRes &result, SleErrorCode errCode) override
    {
        SLE_LOGD("OnFindComplete In");
        Uuid uuid(result.uuid);
        uint8_t type = result.type;
        impl_.callbacks_.ForEach([clientId, connectId, type, &uuid, errCode](sptr<ISleSsapClientCallback> callback) {
            callback->OnFindComplete(clientId, connectId, type, uuid, errCode);
        });
    }

    void OnWriteResult(uint8_t clientId, uint16_t connectId, const ClientWriteRes &result, SleErrorCode errCode) override
    {
#if 0
        SLE_LOGD("OnWriteResult In");
#endif
        uint16_t handle = result.handle;
        uint8_t type = result.type;
        impl_.callbacks_.ForEach([clientId, connectId, handle, type, errCode](sptr<ISleSsapClientCallback> callback) {
            callback->OnWriteResult(clientId, connectId, handle, type, errCode);
        });
    }

    void OnReadResult(uint8_t clientId, uint16_t connectId, const ClientHandleValue &value, SleErrorCode errCode) override
    {
        SLE_LOGD("OnReadResult In");
        SsapClientHandleValue readValue(value);
        impl_.callbacks_.ForEach([clientId, connectId, &readValue, errCode](sptr<ISleSsapClientCallback> callback) {
            callback->OnReadResult(clientId, connectId, readValue, errCode);
        });
    }

    void OnReadByUuidComplete(
        uint8_t clientId, uint16_t connectId, const ClientReadByUuidCmpRes &result, SleErrorCode errCode) override
    {
        SLE_LOGD("OnReadByUuidComplete In");
        Uuid uuid(result.uuid);
        uint8_t type = result.type;
        impl_.callbacks_.ForEach([clientId, connectId, type, &uuid, errCode](sptr<ISleSsapClientCallback> callback) {
            callback->OnReadByUuidComplete(clientId, connectId, type, uuid, errCode);
        });
    }

    void OnExchangeInfo(uint8_t clientId, uint16_t connectId, const SsapMtuInfo &info, SleErrorCode errCode) override
    {
        SLE_LOGD("OnExchangeInfo In");
        uint32_t mtu = info.mtuSize;
        uint16_t version = info.version;
        if (impl_.clientEventHandler_) {
            impl_.clientEventHandler_->PostAsyncTask(
                [clientId, connectId, info, errCode]() {
                    AdvancedSettingManager().GetInstance()->ASProcessSsapcExchangeInfo(clientId, connectId, info, errCode);
                }, 0);
        }
        impl_.callbacks_.ForEach([clientId, connectId, mtu, version, errCode](sptr<ISleSsapClientCallback> callback) {
            callback->OnExchangeInfo(clientId, connectId, mtu, version, errCode);
        });
    }

    void OnNotification(
        uint8_t clientId, uint16_t connectId, const ClientHandleValue &value, SleErrorCode errCode) override
    {
#if 0
        SLE_LOGD("OnNotification In");
#endif
        SsapClientHandleValue notifyValue(value);
        impl_.callbacks_.ForEach([clientId, connectId, &notifyValue, errCode](sptr<ISleSsapClientCallback> callback) {
            callback->OnNotification(clientId, connectId, notifyValue, errCode);
        });
    }

    void OnIndication(
        uint8_t clientId, uint16_t connectId, const ClientHandleValue &value, SleErrorCode errCode) override
    {
#if 0
        SLE_LOGD("OnIndication In");
#endif
        SsapClientHandleValue indicateValue(value);
        impl_.callbacks_.ForEach([clientId, connectId, &indicateValue, errCode](sptr<ISleSsapClientCallback> callback) {
            callback->OnNotification(clientId, connectId, indicateValue, errCode);
        });
    }

private:
    SleSsapClientServer::SleSsapClientServerImpl &impl_;
};

SleSsapClientServer::SleSsapClientServerImpl::SleSsapClientServerImpl()
{
    SLE_LOGI("SleSsapClientServerImpl In");
    callbackImpl_ = std::make_unique<SleSsapClientCallbackImpl>(*this);
    SLE_ASSERT_AND_RETURN_LOG(callbackImpl_, "callbackImpl null error!");

    clientService_ = SleSsapClientService::GetInstance();
    SLE_ASSERT_AND_RETURN_LOG(clientService_, "ssap client service instance null error!");

    SleErrorCode ret = clientService_->RegisterSsapClientCallback(*callbackImpl_.get());
    SLE_ASSERT_AND_RETURN_LOG(ret == SLE_NO_ERROR, "register ssap client callback failed!");

    adapterManager_ = SleAdapterManager::GetInstance();
    SLE_ASSERT_AND_RETURN_LOG(adapterManager_, "adapter manager instance null error!");

    clientEventRunner_ = AppExecFwk::EventRunner::Create(SLE_CLIENT_RUNNER_WORK_THREAD);
    SLE_ASSERT_AND_RETURN_LOG(clientEventRunner_ != nullptr, "create clientEventRunner_ fail");
    clientEventHandler_ = std::make_shared<SleClientEventHandler>(clientEventRunner_);
    SLE_ASSERT_AND_RETURN_LOG(clientEventHandler_ != nullptr, "create clientEventHandler_ fail");
}

SleSsapClientServer::SleSsapClientServerImpl::~SleSsapClientServerImpl()
{
    SLE_LOGI("~SleSsapClientServerImpl In");
    SLE_ASSERT_AND_RETURN_LOG(callbackImpl_, "callbackImpl null error!");
    SLE_ASSERT_AND_RETURN_LOG(clientService_, "ssap client service instance null error!");
    SleErrorCode ret = clientService_->UnregisterSsapClientCallback(*callbackImpl_.get());
    SLE_ASSERT_AND_RETURN_LOG(ret == SLE_NO_ERROR, "unregister ssap client callback failed!");
}

SleErrorCode SleSsapClientServer::SleSsapClientServerImpl::CheckOperation()
{
    SLE_ASSERT_AND_RETURN_RET_LOG(clientService_, SLE_ERR_NOT_READY, "ssap client service instance null error!");
    SLE_ASSERT_AND_RETURN_RET_LOG(adapterManager_, SLE_ERR_NOT_READY, "adapter manager instance null error!");
    int32_t state;
    SleErrorCode ret = adapterManager_->GetState(state);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "get sle state failed!");
    SLE_ASSERT_AND_RETURN_RET_LOG(state == SLE_HOST_STATE_ENABLED, SLE_ERR_STATUS_WRONG,
        "host state(%{public}s) error!", SleDeviceStateStr(state));
    return SLE_NO_ERROR;
}

SleErrorCode SleSsapClientServer::SleSsapClientServerImpl::RegisterSsapClient(
    const Uuid &uuid, uint8_t &clientId, const sptr<ISleSsapClientCallback> &callback)
{
    SLE_LOGD("RegisterSsapClient In");
    auto func = std::bind(&SleSsapClientServer::SleSsapClientServerImpl::UnregisterSsapClientInner,
        this, std::placeholders::_1, std::placeholders::_2);
    SLE_ASSERT_AND_RETURN_RET_LOG(callbacks_.Register(callback, func, clientId), SLE_ERR_FAILED,
        "register ssap client callback failed!");
    SleErrorCode ret = clientService_->RegisterSsapClient(uuid, clientId);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "register ssap client failed!");
    return SLE_NO_ERROR;
}

SleErrorCode SleSsapClientServer::SleSsapClientServerImpl::UnregisterSsapClientInner(
    const sptr<ISleSsapClientCallback> &callback, uint8_t clientId)
{
    return SleSsapClientServer::SleSsapClientServerImpl::UnregisterSsapClient(clientId, callback);
}

SleErrorCode SleSsapClientServer::SleSsapClientServerImpl::UnregisterSsapClient(
    uint8_t clientId, const sptr<ISleSsapClientCallback> &callback)
{
    SLE_LOGD("UnregisterSsapClient In");
    SLE_ASSERT_AND_RETURN_RET_LOG(
        callbacks_.Unregister(callback), SLE_ERR_FAILED, "unregister ssap client callback failed!");
    SleErrorCode ret = clientService_->UnregisterSsapClient(clientId);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "unregister ssap client failed!");
    return SLE_NO_ERROR;
}

SleErrorCode SleSsapClientServer::SleSsapClientServerImpl::FindSsapStructure(
    uint8_t clientId, uint16_t connectId, const SsapClientFindStructure &param)
{
    SLE_LOGD("FindSsapStructure In");
    SleErrorCode ret = clientService_->FindSsapStructure(clientId, connectId, param);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "find ssap structure failed!");
    return SLE_NO_ERROR;
}

SleErrorCode SleSsapClientServer::SleSsapClientServerImpl::ReadRequestByUuid(
    uint8_t clientId, uint16_t connectId, const SsapClientReadReqByUUID &param)
{
    SLE_LOGD("ReadRequestByUuid In");
    SleErrorCode ret = clientService_->ReadRequestByUuid(clientId, connectId, param);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "read request by uuid failed!");
    return SLE_NO_ERROR;
}

SleErrorCode SleSsapClientServer::SleSsapClientServerImpl::ReadRequest(
    uint8_t clientId, uint16_t connectId, uint16_t handle, uint8_t type)
{
    SLE_LOGD("ReadRequest In");
    SleErrorCode ret = clientService_->ReadRequest(clientId, connectId, handle, type);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "read request failed!");
    return SLE_NO_ERROR;
}

SleErrorCode SleSsapClientServer::SleSsapClientServerImpl::WriteRequest(
    uint8_t clientId, uint16_t connectId, const SsapClientHandleValue &value)
{
#if 0
    SLE_LOGD("WriteRequest In");
#endif
    SleErrorCode ret = clientService_->WriteRequest(clientId, connectId, value);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "write request failed!");
    return SLE_NO_ERROR;
}

SleErrorCode SleSsapClientServer::SleSsapClientServerImpl::WriteCommand(
    uint8_t clientId, uint16_t connectId, const SsapClientHandleValue &value)
{
#if 0
    SLE_LOGD("WriteCommand In");
#endif
    SleErrorCode ret = clientService_->WriteCommand(clientId, connectId, value);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "write command failed!");
    return SLE_NO_ERROR;
}

SleErrorCode SleSsapClientServer::SleSsapClientServerImpl::ExchangeInfo(
    uint8_t clientId, uint16_t connectId, uint32_t mtu, uint16_t version)
{
    SLE_LOGD("ExchangeInfo In");
    SleErrorCode ret = clientService_->ExchangeInfo(clientId, connectId, mtu, version);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "exchange info failed!");
    return SLE_NO_ERROR;
}

SleSsapClientServer::SleSsapClientServer()
{
    SLE_LOGD("SleSsapClientServer In");
    pimpl_ = std::make_unique<SleSsapClientServerImpl>();
    SLE_ASSERT_AND_RETURN_LOG(pimpl_, "pimpl null error!");
}

SleSsapClientServer::~SleSsapClientServer(){}

SleErrorCode SleSsapClientServer::RegisterSsapClient(
    const Uuid &uuid, uint8_t &clientId, const sptr<ISleSsapClientCallback> &callback)
{
    SLE_LOGD("RegisterSsapClient In");
    SLE_ASSERT_AND_RETURN_RET_LOG(callback, SLE_ERR_INVALID_PARAM, "callback null error!");
    SLE_ASSERT_AND_RETURN_RET_LOG(pimpl_, SLE_ERR_NOT_READY, "pimpl null error!");
    SleErrorCode ret = pimpl_->RegisterSsapClient(uuid, clientId, callback);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "register ssap client failed!");
    return SLE_NO_ERROR;
}

SleErrorCode SleSsapClientServer::UnregisterSsapClient(uint8_t clientId, const sptr<ISleSsapClientCallback> &callback)
{
    SLE_LOGD("UnregisterSsapClient In");
    SLE_ASSERT_AND_RETURN_RET_LOG(callback, SLE_ERR_INVALID_PARAM, "callback null error!");
    SLE_ASSERT_AND_RETURN_RET_LOG(pimpl_, SLE_ERR_NOT_READY, "pimpl null error!");
    SleErrorCode ret = pimpl_->UnregisterSsapClient(clientId, callback);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "unregister ssap client failed!");
    return SLE_NO_ERROR;
}

SleErrorCode SleSsapClientServer::FindSsapStructure(
    uint8_t clientId, uint16_t connectId, const SsapClientFindStructure &param)
{
    SLE_LOGD("FindSsapStructure In");
    SLE_ASSERT_AND_RETURN_RET_LOG(pimpl_, SLE_ERR_NOT_READY, "pimpl null error!");
    SleErrorCode ret = pimpl_->CheckOperation();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check operation failed!");
    ret = pimpl_->FindSsapStructure(clientId, connectId, param);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "find ssap structure failed!");
    return SLE_NO_ERROR;
}

SleErrorCode SleSsapClientServer::ReadRequestByUuid(
    uint8_t clientId, uint16_t connectId, const SsapClientReadReqByUUID &param)
{
    SLE_LOGD("ReadRequestByUuid In");
    SLE_ASSERT_AND_RETURN_RET_LOG(pimpl_, SLE_ERR_NOT_READY, "pimpl null error!");
    SleErrorCode ret = pimpl_->CheckOperation();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check operation failed!");
    ret = pimpl_->ReadRequestByUuid(clientId, connectId, param);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "read request by uuid failed!");
    return SLE_NO_ERROR;
}

SleErrorCode SleSsapClientServer::ReadRequest(uint8_t clientId, uint16_t connectId, uint16_t handle, uint8_t type)
{
    SLE_LOGD("ReadRequest In");
    SLE_ASSERT_AND_RETURN_RET_LOG(pimpl_, SLE_ERR_NOT_READY, "pimpl null error!");
    SleErrorCode ret = pimpl_->CheckOperation();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check operation failed!");
    ret = pimpl_->ReadRequest(clientId, connectId, handle, type);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "read request failed!");
    return SLE_NO_ERROR;
}

SleErrorCode SleSsapClientServer::WriteRequest(uint8_t clientId, uint16_t connectId, const SsapClientHandleValue &value)
{
    SLE_LOGD("WriteRequest In");
    SLE_ASSERT_AND_RETURN_RET_LOG(pimpl_, SLE_ERR_NOT_READY, "pimpl null error!");
    SleErrorCode ret = pimpl_->CheckOperation();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check operation failed!");
    ret = pimpl_->WriteRequest(clientId, connectId, value);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "write request failed!");
    return SLE_NO_ERROR;
}

SleErrorCode SleSsapClientServer::WriteCommand(uint8_t clientId, uint16_t connectId, const SsapClientHandleValue &value)
{
    SLE_LOGD("WriteCommand In");
    SLE_ASSERT_AND_RETURN_RET_LOG(pimpl_, SLE_ERR_NOT_READY, "pimpl null error!");
    SleErrorCode ret = pimpl_->CheckOperation();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check operation failed!");
    ret = pimpl_->WriteCommand(clientId, connectId, value);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "write command failed!");
    return SLE_NO_ERROR;
}

SleErrorCode SleSsapClientServer::ExchangeInfo(uint8_t clientId, uint16_t connectId, uint32_t mtu, uint16_t version)
{
    SLE_LOGD("ExchangeInfo In");
    SLE_ASSERT_AND_RETURN_RET_LOG(pimpl_, SLE_ERR_NOT_READY, "pimpl null error!");
    SleErrorCode ret = pimpl_->CheckOperation();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check operation failed!");
    ret = pimpl_->ExchangeInfo(clientId, connectId, mtu, version);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "exchange info failed!");
    return SLE_NO_ERROR;
}
} // SleStandard
} // NearLink
} // OHOS